package com.ali.niofiletransfer;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NIOFileTransferClient {
    public static void main(String[] args) {
        try {
            //构建客户端连接
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
            String fileName = "C:\\Users\\Administrator\\Desktop\\test.mobi";
            long startTime = System.currentTimeMillis();
            long total = sendFile(socketChannel, fileName);
            long endTime = System.currentTimeMillis();
            System.out.println("耗时：" + (endTime - startTime) + "，发送字节数：" + total);
            //关闭客户端连接
            socketChannel.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 发送文件
     */
    private static long sendFile(SocketChannel socketChannel, String fileName) {
        return ordinaryWrite(socketChannel, fileName);
//        return windowsZeroCopy(socketChannel, fileName);
//        return linuxZeroCopy(socketChannel, fileName);
    }

    /**
     * 普通写入
     */
    private static long ordinaryWrite(SocketChannel socketChannel, String fileName) {
        //构建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int length;
        long total = 0;
        Path path = Paths.get(fileName);
        //构建文件通道
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
            while (true) {
                //写入内容至缓冲区
                length = fileChannel.read(byteBuffer);
                if (length > 0) {
                    //读写切换
                    byteBuffer.flip();
                    //写入内容至客户端连接
                    socketChannel.write(byteBuffer);
                    //清理缓冲区
                    byteBuffer.clear();
                    total = total + length;
                } else if (length == 0) {
                    break;
                } else if (length == -1) {
                    break;
                }
            }
            return total;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Windows零拷贝
     * 一次只能发送8M
     */
    private static long windowsZeroCopy(SocketChannel socketChannel, String fileName) {
        Path path = Paths.get(fileName);
        //构建文件通道
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
            int eightM = 8 * 1024 * 1024;
            int size = (int) (fileChannel.size() / eightM);
            int remainder = (int) (fileChannel.size() % eightM);
            if (remainder > 0) {
                size = size + 1;
            }
            for (int i = 1; i <= size; i++) {
                long start = (long) (i - 1) * eightM;
                long end = (long) i * eightM;
                if (i == size) {
                    end = fileChannel.size();
                }
                //零拷贝
                fileChannel.transferTo(start, end, socketChannel);
            }
            return fileChannel.size();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Linux零拷贝
     */
    private static long linuxZeroCopy(SocketChannel socketChannel, String fileName) {
        Path path = Paths.get(fileName);
        //构建文件通道
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
            //零拷贝
            fileChannel.transferTo(0, fileChannel.size(), socketChannel);
            return fileChannel.size();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
